import java.io.IOException;
import java.awt.Event;
import java.util.List;

import com.jme3.animation.AnimChannel;
import com.jme3.animation.AnimControl;
import com.jme3.animation.AnimEventListener;
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionListener;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.PhysicsRayTestResult;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.collision.CollisionResult;
import com.jme3.collision.CollisionResults;
import com.jme3.effect.ParticleEmitter;
import com.jme3.input.ChaseCamera;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Ray;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.post.filters.BloomFilter;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.control.Control;
import com.jme3.scene.shape.Sphere;
import com.jme3.network.Client;
import com.jme3.network.Network;
import com.jme3.network.Server;
import com.jme3.system.JmeContext;
import com.jme3.system.JmeContext.Type;



public class ClientMain extends SimpleApplication implements ActionListener, PhysicsCollisionListener, AnimEventListener {
	CharacterControl character;
    Node model;
    //temp vectors
    Vector3f walkDirection = new Vector3f();
    //animation
    AnimChannel animationChannel;
    AnimChannel shootingChannel;
    AnimControl animationControl;
    float airTime = 0;
    //camera
    boolean left = false, right = false, up = false, down = false, open = false;
    ChaseCamera chaseCam;
    int door1 = 0;
    Geometry door1G;
    private BulletAppState bulletAppState;
    Level level;
    

	public static void main(String[] args) {
		ClientMain app = new ClientMain();
		app.start(JmeContext.Type.Display); // standard display type
	}

		public void simpleInitApp() {
			setupKeys();
	        createCharacter();
	        setupChaseCamera();
		    createLight();
		    setupFilter();
	        setupAnimationController();
			Client myClient = null;
			bulletAppState = new BulletAppState();
		    bulletAppState.setThreadingType(BulletAppState.ThreadingType.PARALLEL);
		    stateManager.attach(bulletAppState);
			level = new Level(assetManager, bulletAppState);
			rootNode.attachChild(level.makeSpaceStation());
			try {
				myClient = Network.connectToServer("localhost", 5000);
			} catch (IOException e) {
				e.printStackTrace();
			}
			myClient.start();

		}
		
		 private void setupKeys() {
		        inputManager.addMapping("wireframe", new KeyTrigger(KeyInput.KEY_T));
		        inputManager.addListener(this, "wireframe");
		        inputManager.addMapping("CharLeft", new KeyTrigger(KeyInput.KEY_A));
		        inputManager.addMapping("CharRight", new KeyTrigger(KeyInput.KEY_D));
		        inputManager.addMapping("CharUp", new KeyTrigger(KeyInput.KEY_W));
		        inputManager.addMapping("CharDown", new KeyTrigger(KeyInput.KEY_S));
		        inputManager.addMapping("CharSpace", new KeyTrigger(KeyInput.KEY_SPACE));
		        inputManager.addMapping("DoorOpen", new KeyTrigger(KeyInput.KEY_O));
		        inputManager.addMapping("Shoot", new KeyTrigger(KeyInput.KEY_E));
		        inputManager.addListener(this, "CharLeft");
		        inputManager.addListener(this, "CharRight");
		        inputManager.addListener(this, "CharUp");
		        inputManager.addListener(this, "CharDown");
		        inputManager.addListener(this, "CharSpace");
		        inputManager.addListener(this, "Shoot");
		        inputManager.addListener(this, "CharOpen");
		    }
		 
		 private void createCharacter() {
		        CapsuleCollisionShape capsule = new CapsuleCollisionShape(1f, 3f);
		        character = new CharacterControl(capsule, 0.01f);
		        model = (Node) assetManager.loadModel("Models/Oto/Oto.mesh.xml");
		        model.setLocalScale(0.5f);
		        model.addControl(character);
		        character.setPhysicsLocation(new Vector3f(5f, 5f, 15f));
		        rootNode.attachChild(model);
		    }
		 
		 private void setupChaseCamera() {
		        flyCam.setEnabled(false);
		        chaseCam = new ChaseCamera(cam, model, inputManager);
		        chaseCam.setMinVerticalRotation(0.9f);
		        chaseCam.setDefaultVerticalRotation(0.9f);
				chaseCam.setToggleRotationTrigger(new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
		    }
		 
		 private void setupAnimationController() {
		        animationControl = model.getControl(AnimControl.class);
		        animationControl.addListener(this);
		        animationChannel = animationControl.createChannel();
		        shootingChannel = animationControl.createChannel();
		        shootingChannel.addBone(animationControl.getSkeleton().getBone("uparm.right"));
		        shootingChannel.addBone(animationControl.getSkeleton().getBone("arm.right"));
		        shootingChannel.addBone(animationControl.getSkeleton().getBone("hand.right"));
		    }
		 
		 @Override
		    public void simpleUpdate(float tpf) {
		    	Vector3f camDir = cam.getUp().clone().multLocal(0.1f).add(cam.getDirection().clone().multLocal(0.1f));
		        Vector3f camLeft = cam.getLeft().clone().multLocal(0.1f);
		        camDir.y = 0;
		        camLeft.y = 0;
		        walkDirection.set(0, 0, 0);
		        if (left) {
		            walkDirection.addLocal(camLeft);
		        }
		        if (right) {
		            walkDirection.addLocal(camLeft.negate());
		        }
		        if (up) {
		            walkDirection.addLocal(camDir);
		        }
		        if (down) {
		            walkDirection.addLocal(camDir.negate());
		        }
		        if (!character.onGround()) {
		            airTime = airTime + tpf;
		        } else {
		            airTime = 0;
		        }
		        if (walkDirection.length() == 0) {
		            if (!"stand".equals(animationChannel.getAnimationName())) {
		                animationChannel.setAnim("stand", 1f);
		            }
		        } else {
		            character.setViewDirection(walkDirection);
		            if (airTime > .3f) {
		                if (!"stand".equals(animationChannel.getAnimationName())) {
		                    animationChannel.setAnim("stand");
		                }
		            } else if (!"Walk".equals(animationChannel.getAnimationName())) {
		                animationChannel.setAnim("Walk", 1f);
		            }
		        }
		        character.setWalkDirection(walkDirection);
		        
		        if(door1G != null){
					if(door1==-1){
						door1G.setLocalTranslation(door1G.getLocalTranslation().add(-tpf*2, 0f, 0f));
					}
					else if (door1==1){
						door1G.setLocalTranslation(door1G.getLocalTranslation().add(tpf*2, 0f, 0f));
					}
					
			        if(door1G.getLocalTranslation().x<-5f&&door1!=0){
			        	open=false;
			        	door1=0;
			        } 
			        else if(door1G.getLocalTranslation().x>4.5f&&door1!=0){
			        	open=true;
			        	door1=0;
			        }
		        }
		    }
		 
		 public void onAction(String binding, boolean value, float tpf) {
		        if (binding.equals("CharLeft")) {
		            if (value) {
		                left = true;
		            } else {
		                left = false;
		            }
		        } else if (binding.equals("CharRight")) {
		            if (value) {
		                right = true;
		            } else {
		                right = false;
		            }
		        } else if (binding.equals("CharUp")) {
		            if (value) {
		                up = true;
		            } else {
		                up = false;
		            }
		        } else if (binding.equals("CharDown")) {
		            if (value) {
		                down = true;
		            } else {
		                down = false;
		            }
		        } else if (binding.equals("CharSpace")) {
		            character.jump();
		        } else if (binding.equals("Shoot") && !value) {	
		  			CollisionResults results = new CollisionResults();
		  			Vector2f click2d = inputManager.getCursorPosition();
		  			Vector3f click3d = cam.getWorldCoordinates(
		  			    new Vector2f(click2d.x, click2d.y), 0f).clone();
		  			Vector3f dir = cam.getWorldCoordinates(
		  			    new Vector2f(click2d.x, click2d.y), 1f).subtractLocal(click3d).normalizeLocal();
		  			Ray ray = new Ray(click3d, dir);
		  			rootNode.collideWith(ray, results);
		  			System.out.println("----- Collisions? " + results.size() + "-----");
		  			if (results.size() > 0) {
		  				// The closest collision point is what was truly hit:
		  				CollisionResult closest = results.getClosestCollision();
		  				Geometry g = closest.getGeometry();	
		  				if(g.getName().equals("Door")&&!open){  	
		  					door1G=g;
		  					door1 = 1;
		  				} 
		  				else if(g.getName().equals("Door")&&open){
		  					door1G=g;
							door1 = -1;
		  				}
		  			}
		        }
		    }
		    
		    public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
		        if (channel == shootingChannel) {
		            channel.setAnim("stand");
		        }
		    }

		    public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {
		    }

			@Override
			public void collision(PhysicsCollisionEvent arg0) {
				// TODO Auto-generated method stub
				
			}
			
			private void createLight() {
		        Vector3f direction = new Vector3f(0.5f, -0.7f, -1).normalizeLocal();
		        DirectionalLight dl = new DirectionalLight();
		        dl.setDirection(direction);
		        dl.setColor(new ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f));
		        rootNode.addLight(dl);
		        
		        Vector3f direction2 = new Vector3f(-0.5f, -0.7f, 0.5f).normalizeLocal();
		        DirectionalLight d2 = new DirectionalLight();
		        d2.setDirection(direction2);
		        d2.setColor(new ColorRGBA(0.2f, 0.2f, 0.2f, 1.0f));
		        rootNode.addLight(d2);
		    }
			
			private void setupFilter() {
		        FilterPostProcessor fpp = new FilterPostProcessor(assetManager);
		        BloomFilter bloom = new BloomFilter(BloomFilter.GlowMode.Objects);
		        fpp.addFilter(bloom);
		        viewPort.addProcessor(fpp);
		    }
	}